﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;

namespace Ratatoskr
{
    public class InputStrobe : UpdateServer<InputServer>
    {
        public InputStrobe()
            : base("InputStrobe")
        {
        }
    }

    public abstract class InputServer : ServerComponent<Component>
    {
        public InputServer(string type, bool single = false)
            : base(type, false, single)
        {
            AddServerProfile("InputStrobe", Update);
        }

        protected abstract void Update(object sender, ServerComponentEventArgs e);
    }

    public class MousePositionServer : InputServer
    {
        public MousePositionServer(string type = "MousePosition", bool single = true)
            : base(type, single)
        {
        }

        protected override void Update(object sender, ServerComponentEventArgs e)
        {
            MouseState state = Mouse.GetState();
            DoTrigger(new object[] { state.X, state.Y });
        }
    }

    public class MouseDeltaServer : InputServer
    {
        protected int _x;
        protected int _y;
        protected bool _strict;
        protected GraphicsDevice _device;
        protected MouseState _state;

        public MouseDeltaServer(bool strict = true, string type = "MouseDelta", bool single = true)
            : base(type, single)
        {
            SetStrict(strict);
        }

        public void SetStrict(bool strict)
        {
            _strict = strict;
            Refresh();
            Cleanup();
        }

        protected void Refresh()
        {
            _device = _parent.GraphicsDevice;
            _state = Mouse.GetState();
        }

        protected void Cleanup()
        {
            if (_strict)
            {
                _x = _parent.GraphicsDevice.DisplayMode.Width / 2;
                _y = _parent.GraphicsDevice.DisplayMode.Height / 2;
                Mouse.SetPosition(_x, _y);
            }
            else
            {
                _x = _state.X;
                _y = _state.Y;
            }
        }

        protected override void Update(object sender, ServerComponentEventArgs e)
        {
            Refresh();
            int x = _state.X - _x;
            int y = _state.Y - _y;
            Cleanup();
            DoTrigger(new object[] { x, y });
        }
    }

    public class MouseWheelDeltaServer : InputServer
    {
        protected int _wheel;
        protected int _res;

        public MouseWheelDeltaServer(int resolution = 120, string type = "MouseWheelDelta", bool single = true)
            : base(type, single)
        {
            _wheel = Mouse.GetState().ScrollWheelValue;
            _res = resolution;
        }

        public void SetResolution(int resolution)
        {
            _res = resolution;
        }

        protected override void Update(object sender, ServerComponentEventArgs e)
        {
            int wheel = Mouse.GetState().ScrollWheelValue;
            if (Math.Abs(wheel - _wheel) < _res)
                return;
            int delta = (wheel - _wheel) / _res;
            _wheel = wheel;
            DoTrigger(new object[] { delta });
        }
    }

    public enum ButtonStateEX
    {
        Released = 0,
        Held = 1,
        Tapped = 2,
        DoubleTapped = 3
    }

    public abstract class ButtonServer : InputServer
    {
        int _holdDelay;
        public int HoldDelay
        {
            get { return _holdDelay; }
            set { _holdDelay = value; }
        }
        int _doubleDelay;
        public int DoubleTapDelay
        {
            get { return _doubleDelay; }
            set { _doubleDelay = value; }
        }

        int _lastPressed;
        bool _wasPressed;
        ButtonStateEX _previousState = ButtonStateEX.Released;

        public ButtonServer(string type, int holdDelay, int doubleTapDelay, bool single = false)
            : base(type, single)
        {
            _holdDelay = holdDelay;
            _doubleDelay = doubleTapDelay;
        }

        protected override void Update(object sender, ServerComponentEventArgs e)
        {
            GameTime gt;
            try { gt = (GameTime)e.Args[0]; }
            catch (InvalidCastException x) { throw new ArgumentException("Expected GameTime as first server argument.", x); }
            int time = gt.TotalGameTime.Milliseconds;
            bool pressed = IsButtonPressed();
            ButtonStateEX state = GetButtonStateEX(time, pressed);
            DoTrigger(new object[] { state, (state != _previousState) });
            if (pressed & !_wasPressed)
                _lastPressed = time;
            _wasPressed = pressed;
            _previousState = state;
        }

        protected ButtonStateEX GetButtonStateEX(int time, bool pressed)
        {
            if (!pressed)
                return ButtonStateEX.Released;
            if (!_wasPressed)
                if (time - _lastPressed <= _doubleDelay)
                    return ButtonStateEX.DoubleTapped;
                else if (_holdDelay > 0)
                    if (_previousState == ButtonStateEX.DoubleTapped)
                        return ButtonStateEX.DoubleTapped;
                    else return ButtonStateEX.Tapped;
            if (time - _lastPressed <= _holdDelay)
                if (_previousState == ButtonStateEX.DoubleTapped)
                    return ButtonStateEX.DoubleTapped;
                else return ButtonStateEX.Tapped;
            return ButtonStateEX.Held;
        }

        protected abstract bool IsButtonPressed();
    }

    public class KeypressServer : ButtonServer
    {
        protected Keys _key;

        public KeypressServer(Keys key, string type, int holdDelay = 500, int doubleTapDelay = 0, bool single = true)
            : base(type, holdDelay, doubleTapDelay, single)
        {
            _key = key;
        }

        protected override bool IsButtonPressed()
        {
            return Keyboard.GetState().IsKeyDown(_key);
        }
    }

    public class KeyComboServer : KeypressServer
    {
        protected Keys _mod;

        public KeyComboServer(Keys modifier, Keys key, string type, int holdDelay = 500, int doubleTapDelay = 0, bool single = false)
            : base(key, type, holdDelay, doubleTapDelay, single)
        {
            _mod = modifier;
        }

        protected override bool IsButtonPressed()
        {
            return base.IsButtonPressed() && Keyboard.GetState().IsKeyDown(_mod);
        }
    }

    public class MouseLeftButtonServer : ButtonServer
    {
        public MouseLeftButtonServer(string type, int holdDelay = 250, int doubleTapDelay = 500, bool single = false)
            : base(type, holdDelay, doubleTapDelay, single)
        {
        }

        protected override bool IsButtonPressed()
        {
            return (Mouse.GetState().LeftButton == ButtonState.Pressed);
        }
    }

    public class MouseRightButtonServer : ButtonServer
    {
        public MouseRightButtonServer(string type, int holdDelay = 250, int doubleTapDelay = 500, bool single = false)
            : base(type, holdDelay, doubleTapDelay, single)
        {
        }

        protected override bool IsButtonPressed()
        {
            return (Mouse.GetState().RightButton == ButtonState.Pressed);
        }
    }

    public class MouseMiddleButtonServer : ButtonServer
    {
        public MouseMiddleButtonServer(string type, int holdDelay = 250, int doubleTapDelay = 500, bool single = false)
            : base(type, holdDelay, doubleTapDelay, single)
        {
        }

        protected override bool IsButtonPressed()
        {
            return (Mouse.GetState().MiddleButton == ButtonState.Pressed);
        }
    }

    public class MouseBackButtonServer : ButtonServer
    {
        public MouseBackButtonServer(string type, int holdDelay = 250, int doubleTapDelay = 500, bool single = false)
            : base(type, holdDelay, doubleTapDelay, single)
        {
        }

        protected override bool IsButtonPressed()
        {
            return (Mouse.GetState().XButton1 == ButtonState.Pressed);
        }
    }

    public class MouseForwardButtonServer : ButtonServer
    {
        public MouseForwardButtonServer(string type, int holdDelay = 250, int doubleTapDelay = 500, bool single = false)
            : base(type, holdDelay, doubleTapDelay, single)
        {
        }

        protected override bool IsButtonPressed()
        {
            return (Mouse.GetState().XButton2 == ButtonState.Pressed);
        }
    }
}